package com.jiufengxinxi.ts.device.interfaces;

import com.jiufengxinxi.ts.common.exception.BusinessException;
import com.jiufengxinxi.ts.common.utils.StreamUtil;
import com.jiufengxinxi.ts.device.callback.IDeviceCallback;
import gnu.io.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Enumeration;
import java.util.TooManyListenersException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;


/**
 * 串口抽象类
 * @author roger
 *
 */
public abstract class ICommDevice extends IDviceGeneral implements Runnable,SerialPortEventListener {
	
	private static Logger logger= LoggerFactory.getLogger(ICommDevice.class);
	
	private String port;
	
	private CommPortIdentifier commPort;
	
	private SerialPort serialPort;

	private int baudrate = 0;

	/**
	 * 接收字节数组
	 */
	private boolean reciveBytes = false;

	/**
	 * 同步
	 */
	private boolean synchronizationData = false;


	/**
	 * 编码
	 */
	private String coded = "UTF-8";
	
	private boolean connect=false;
	
	private String msg="连接异常";

	
	private String appName = "COM DEVICE";
    private int timeout = 2000;//open 端口时的等待时间
    private int threadTime = 0;
    private InputStream inputStream;
    private OutputStream outputStream;

	private BlockingQueue<byte[]> bsdataQue = new ArrayBlockingQueue<byte[]>(2000);

	public ICommDevice(String port, IDeviceCallback deviceCallback) {
		this.port=port;
		setDeviceCallback(deviceCallback);
		initDevice();
	}

	public ICommDevice(String port,int baudrate,IDeviceCallback deviceCallback) {
		this.port=port;
		this.baudrate=baudrate;
		setDeviceCallback(deviceCallback);
		initDevice();
	}

	public ICommDevice(String port) {
		this.port=port;
		initDevice();
	}

	public void initDevice(){
		System.out.println("初始化串口设备");
		if(port==null){
			return;
		}
		setReConnCount(10);
		logger.info("初始化串口设备");
		//initDevide();
		//selectPort();
		new Thread(new Runnable() {
			@Override
			public void run() {
				while(true){
					try{
						if(bsdataQue.size()>0) {
							byte[] data = bsdataQue.poll();
							if(!reciveBytes){
								produceSinal(new String(data, coded).trim());
							}else {
								produceSinal(data);
							}
						}

						Thread.sleep(10);
					}catch (Exception e){}
				}
			}
		}).start();
	}
	
	@Override
	public void initDevide(String... param) {
		System.out.println("初始化串口设备"+port);
		if(port==null){
			return;
		}
		logger.info("初始化串口设备:"+port);
		appName=getDeviceName()+port;
		//selectPort();
		startup();
//		if(!connect){
//			ExecutorServiceUtils.execute(new BaseThreadRunnable() {
//				@Override
//				public void running() {
//					int i=0;
//					while(i++<10&&!connect){
//						startup();
//					}
//				}
//				
//				@Override
//				public boolean isUseLock() {
//					return false;
//				}
//				
//				@Override
//				public String getName() {
//					return "串口重连";
//				}
//			});
//		}
	}
	
	@Override
	public void startup() {
		if(port==null){
			logger.error("串口为空，无法启动");
			return;
		}
		logger.info("启动串口设备:"+port);
		int state=-1;
		int rc=getReConnCount();
		logger.info(port+"开始连接，将连接"+rc+"次，直至连接成功");
		do{
			logger.info(port+"第"+(getReConnCount()-rc+1)+"次连接");
			state=selectPort();
			if(state==1){
				logger.error("请确认"+port+"有接入设备");
				//continue;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			rc--;
		}while(state!=0&&rc>=0);
		if(state!=0){
			logger.error(port+"连接失败,连接"+(getReConnCount()-rc)+"次");
			return;
		}else {
			logger.info("连接"+port+" ok");
		}
    	openPort();
    	startRead();
	}
	
	public int selectPort(){
		close();
          this.commPort = null;
          try {
        	  Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers(); 
              //将可用串口名添加到List并返回该List
              while (portList.hasMoreElements()) {
            	  String tport=portList.nextElement().getName();
                  //System.out.println("找到串口："+tport);
                  if(tport.equals(port)){
                	  commPort= CommPortIdentifier.getPortIdentifier(port);

                	  break;
                  }
              }
              
              if(commPort==null){
            	  connect=false;
      			  msg="不存在";
      			  logger.error(port+"不存在");
      			  return 1;
              }
              return 0;
		} catch (NoSuchPortException e) {
			connect=false;
			msg="不存在";
			logger.error(port+"不存在或已经断开:"+e.getMessage());
			return 2;
		} 
	}
	
	
	public void openPort(){
		if(commPort == null)
              System.out.println(String.format("无法找到名字为'%1$s'的串口！", port));
          else{
        	  System.out.println("端口选择成功，当前端口："+port+",现在实例化 SerialPort:");
              
              try{
                  serialPort = (SerialPort)commPort.open(appName, 300);
                  if(baudrate!=0){
					  serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
				  }
                  connect=true;
                  System.out.println("实例 SerialPort 成功！");
             }catch(PortInUseException e){
            	 connect=false;
            	 msg="被占用";
                 /*throw new BusinessException(String.format("端口'%1$s'正在使用中！", 
                         commPort.getName()));*/
             }catch (UnsupportedCommOperationException e) {
   			  connect=false;
   			  msg="波特率设置失败";
   			  logger.error(port+"波特率设置失败:"+baudrate+"\n"+e.getMessage());
   		  	}catch (Exception e) {
            	 connect=false;
            	 msg="打开端口失败";
			}
         }
	}
	
	
	/**
      * @方法名称 :checkPort
      * @功能描述 :检查端口是否正确连接
      * @返回值类型 :void
      */
     public boolean checkPort(){
         if(commPort == null){
        	 logger.error(appName+" 未打开");
        	 msg="未连接";
			 //logger.info("串口未连接");
        	 return false;
         }
            // throw new BusinessException("无端口");
         
         if(serialPort == null){
        	 msg="未连接";
        	 logger.error(appName+" 端口未打开");
             return false;
         }
         return true;
     }
     
     
     /**
           * @方法名称 :write
           * @功能描述 :向端口发送数据，请在调用此方法前 先选择端口，并确定SerialPort正常打开！
           * @返回值类型 :void
           *    @param message
           */
      public void write(String message) {
		  try {
			  OutputStreamWriter writer = new OutputStreamWriter(serialPort.getOutputStream(), coded);
			  writer.write(message);
			  writer.flush();
		  } catch (IOException e) {
			  e.printStackTrace();
		  }
		  //write(message.getBytes());
      }

	public void write(byte[] message) {
		synchronized (getGateNo()) {
			if(!checkPort()){
				connect=false;
				return;
			}

			try{
				outputStream = new BufferedOutputStream(serialPort.getOutputStream());
			}catch(IOException e){
				connect=false;
				msg="连接异常";
				throw new BusinessException("获取端口的OutputStream出错："+e.getMessage());
			}

			try{
				outputStream.write(message);
				//log("信息发送成功！");
			}catch(IOException e){
				connect=false;
				msg="连接异常";
				throw new BusinessException("向端口发送信息时出错："+e.getMessage());
			}finally{
				try{
					outputStream.flush();
					//outputStream.close();
				}catch(Exception e){
					connect=false;
					msg="连接异常";
					logger.error("端口连接错误");
				}
			}
		}
	}
      
      
      /**
            * @方法名称 :startRead
            * @功能描述 :开始监听从端口中接收的数据
            * @返回值类型 :void
            *    @param time  监听程序的存活时间，单位为秒，0 则是一直监听
            */
      public void startRead(int time){
    	  if(!checkPort()){
        	  return;
          }

          try{
              inputStream = new BufferedInputStream(serialPort.getInputStream());
          }catch(IOException e){
        	  msg="监听失败";
              throw new BusinessException("获取端口的InputStream出错："+e.getMessage());
          }

          try{
              serialPort.addEventListener(this);
          }catch(TooManyListenersException e){
              throw new BusinessException(e.getMessage());
          }

          serialPort.notifyOnDataAvailable(!synchronizationData);

          log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
          if(time > 0){
              this.threadTime = time;
              if(this.threadTime!=0){
            	  Thread t = new Thread(this);
                  t.start();
                  log(String.format("监听程序将在%1$d秒后关闭。。。。", threadTime));
              }

          }
      }
      
      
      public void startRead(){
    	  if(!checkPort()){
        	  return;
          }
          
          try{
              inputStream = new BufferedInputStream(serialPort.getInputStream());
          }catch(IOException e){
        	  msg="监听失败";
              throw new BusinessException("获取端口的InputStream出错："+e.getMessage());
          }
          
          try{
              serialPort.addEventListener(this);
          }catch(TooManyListenersException e){
              throw new BusinessException(e.getMessage());
          }
          
          serialPort.notifyOnDataAvailable(!synchronizationData);
          //serialPort.notifyOnRingIndicator(!synchronizationData);
          //serialPort.get
          
          log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
          
      }
      
      /**
       * @方法名称 :close
       * @功能描述 :关闭 SerialPort
       * @返回值类型 :void
       */
      public void close(){
    	  if(serialPort!=null){
    		  try {
    			  if(inputStream!=null){
    				  inputStream.close();
    				  inputStream=null;
    			  }
    			  if(outputStream!=null) {
					  outputStream.close();
					  outputStream = null;
				  }
			  } catch (IOException e) {
				  logger.error(port+"关闭失败:"+e.getMessage());
			  }
			  if(serialPort!=null){
				  serialPort.notifyOnDataAvailable(false);
				  serialPort.removeEventListener();
				  serialPort.close();
			  }

              serialPort = null;
              commPort = null;
              msg="连接异常";
    	  }
      }
      
      public void log(String msg){
           //System.out.println(port+" --> "+msg);
       }
      
      
      /**
       * 数据接收的监听处理函数
       */
      @Override
      public void serialEvent(SerialPortEvent arg0) {
          switch(arg0.getEventType()){
	          case SerialPortEvent.BI:/*Break interrupt,通讯中断*/ connect=false; msg="通讯中断"; break;
	          case SerialPortEvent.OE:/*Overrun error，溢位错误*/  connect=false; msg="溢位错误";  break;
	          case SerialPortEvent.FE:/*Framing error，传帧错误*/  connect=false; msg="传帧错误";  break;
	          case SerialPortEvent.PE:/*Parity error，校验错误*/	 connect=false; msg="校验错误"; break;
	          case SerialPortEvent.CD:/*Carrier detect，载波检测*/	 connect=false; msg="载波检测"; break;
	          case SerialPortEvent.CTS:/*Clear to send，清除发送*/  connect=false; msg="清除发送";  break;
	          case SerialPortEvent.DSR:/*Data set ready，数据设备就绪*/ connect=true; msg="连接正常"; break;
	          case SerialPortEvent.RI:/*Ring indicator，响铃指示*/break;
	          case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*Output buffer is empty，输出缓冲区清空*/ 
	              break;
	          case SerialPortEvent.DATA_AVAILABLE:/*Data available at the serial port，端口有可用数据。读到缓冲数组，输出到终端*/
	        	  connect=true; msg="连接正常";
	              byte[] bdata=null;
	              //String s2 = "";
				  if (!synchronizationData) {
	              	try {


//						  byte[] readBuffer = new byte[1024];
//						  int len = 0;
//						  while ((len = inputStream.read(readBuffer)) != -1) {
//							  //System.out.println("实时反馈：" + new String(readBuffer, 0, len).trim() + new Date());
//							  readStr += new String(readBuffer, 0, len, coded).trim();
//							  break;
//						  }
						bdata = readData();

						bsdataQue.offer(bdata);

						  //log("接收到端口返回数据(长度为" + readStr.length() + ")：" + readStr);




					  } catch(Exception e){
						  close();
						  logger.error("从串口读数据异常:"+port);
					  }

				  }
          }
      }


      public byte[] readData() throws IOException {
		  ByteArrayOutputStream output = new ByteArrayOutputStream();
		  StreamUtil.copy(inputStream,output);
		  return output.toByteArray();
	  }
      
      
      public byte[] readData(int minlen,int timeoutMillis) throws IOException {
		  ByteArrayOutputStream output = new ByteArrayOutputStream();
		  StreamUtil.copy(inputStream,output,minlen,timeoutMillis);
		  return output.toByteArray();
	  }
      
      
    @Override
  	public void run() {
  		while(threadTime-->0){
  			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
  		}
  		serialPort.notifyOnDataAvailable(false);
  	}
      
    @Override
  	public String status() {
  		return isConnect()?"连接正常":msg;
  	}

  	@Override
  	public boolean check() {
  		return isConnect();
  	}
      
      
    public abstract void produceSinal(String sinal);

	public abstract void produceSinal(byte[] sinal);

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}

	public CommPortIdentifier getCommPort() {
		return commPort;
	}

	public void setCommPort(CommPortIdentifier commPort) {
		this.commPort = commPort;
	}

	public SerialPort getSerialPort() {
		return serialPort;
	}

	public void setSerialPort(SerialPort serialPort) {
		this.serialPort = serialPort;
	}

	public boolean isConnect() {
		//write("test");
		return connect;
	}

	public void setConnect(boolean connect) {
		this.connect = connect;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public String getAppName() {
		return appName;
	}

	public void setAppName(String appName) {
		this.appName = appName;
	}

	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	public int getThreadTime() {
		return threadTime;
	}

	public void setThreadTime(int threadTime) {
		this.threadTime = threadTime;
	}

	public InputStream getInputStream() {
		return inputStream;
	}

	public void setInputStream(InputStream inputStream) {
		this.inputStream = inputStream;
	}

	public OutputStream getOutputStream() {
		return outputStream;
	}

	public void setOutputStream(OutputStream outputStream) {
		this.outputStream = outputStream;
	}

	public int getBaudrate() {
		return baudrate;
	}

	public void setBaudrate(int baudrate) {
		this.baudrate = baudrate;
	}

	public boolean isReciveBytes() {
		return reciveBytes;
	}

	public void setReciveBytes(boolean reciveBytes) {
		this.reciveBytes = reciveBytes;
	}

	public boolean isSynchronizationData() {
		return synchronizationData;
	}

	public void setSynchronizationData(boolean synchronizationData) {
		this.synchronizationData = synchronizationData;
	}

	public String getCoded() {
		return coded;
	}

	public void setCoded(String coded) {
		this.coded = coded;
	}
}
